package com.mall.malladminbackend.config;

import com.mall.malladminbackend.config.entrypoint.Http401UnauthorizedEntryPoint;
import com.mall.malladminbackend.filters.JwtFilter;
import com.mall.malladminbackend.utils.JwtUtils;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.management.MXBean;

/**
 * @EnableGlobalMethodSecurity(prePostEnabled = true) 作用表示让 @PreAuthorize 和 @PostAuthorize 这两个注解生效
 */
@EnableWebSecurity // 开启Security的自定义配置
@Configuration
@AllArgsConstructor
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private JwtUtils jwtUtils;
    private Http401UnauthorizedEntryPoint point;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 控制某些接口是否可以匿名访问以及其他的配置主要就在该方法中，匿名访问的意思就是可以不登录就可以访问
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        /**
         * 1. authorizeRequests() 先调用该方法，在该方法后续配置哪些接口需要认证才能，哪些接口可以匿名访问
         * 2. 一旦调用 authorizeRequests() 默认会禁用掉 Spring Security 自带的那个登录页面。
         */
        /**
        http.authorizeRequests()
                .antMatchers("/greet").permitAll()  // permitAll() 表示可以匿名访问
                .antMatchers("/user/**").authenticated() // authenticated() 表示必须要认证之后才能访问
                .and() // and() 表示某个块的配置与另外一块配置分隔用的
                .formLogin();  // *** 在前后端分离的场景下，这个默认是要禁用的，目前暂时使用自带的登录页面
        */
        JwtFilter jwtFilter = new JwtFilter();
        jwtFilter.setJwtUtils(jwtUtils);

        /**
         * 1.spring security是通过一条过滤器链来实现整个认证和授权的过程。
         * 2.UsernamePasswordAuthenticationFilter 是整个过滤器链中其中一个过滤器。
         * 3.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
         *   就是jwtFilter添加到 UsernamePasswordAuthenticationFilter 这个过滤器之前
         * 4.为什么加在 UsernamePasswordAuthenticationFilter 他之前，因为整个过滤器链我们知道名字就它。
         */
        http.cors().configurationSource(corsConfigurationSource()) // 配置spring security的跨域问题
                .and()
                .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)  //
                .authorizeRequests()
                .antMatchers("/login").permitAll()
                .antMatchers("/menu/**", "/sys-user/**").authenticated()
//                .antMatchers("/greet/**", "/user/**", "/company/**").permitAll()
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(point)   // 解决未登录访问，返回403问题
                .and()
                .csrf().disable();  // spring security针对所有的post请求都做了 csrf() 攻击防护
    }


    // 解决 spring security 的跨域问题的
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfigurationSource source =   new UrlBasedCorsConfigurationSource();
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("*");    //同源配置，*表示任何请求都视为同源，若需指定ip和端口可以改为如“localhost：8080”，多个以“，”分隔；
        corsConfiguration.addAllowedHeader("*");//header，允许哪些header，本案中使用的是token，此处可将*替换为token；
        corsConfiguration.addAllowedMethod("*");    //允许的请求方法，PSOT、GET等
        ((UrlBasedCorsConfigurationSource) source).registerCorsConfiguration("/**",corsConfiguration); //配置允许跨域访问的url
        return source;
    }
}
